#include <cmath>
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include "rational.h"

long rational::gcd(long a, long b) const
{
	a = std::abs(a);
	b = std::abs(b);

	while (b) {
		long c = a % b;
		a = b;
		b = c;
	}

	return a;
}

void rational::normalize()
{
	bool sign = (numerator*denumerator < 0);

	assert(denumerator != 0);

	numerator = std::abs(numerator);
	denumerator = std::abs(denumerator);

	int div = gcd(numerator, denumerator);
	numerator /= div;
	denumerator /= div;

	if (sign) numerator = - numerator;
}

rational::rational(int numerator, int denumerator)
: numerator(numerator), denumerator(denumerator)
{
	normalize();
}

void rational::swap(rational &obj)
{
	std::swap(numerator, obj.numerator);
	std::swap(denumerator, obj.denumerator);
}

rational & rational::operator=(rational obj)
{
	swap(obj);
	return *this;
}

rational & rational::operator+=(const rational &obj)
{
	assert(denumerator != 0);
	assert(obj.denumerator != 0);

	numerator = numerator*obj.denumerator
			+ obj.numerator*denumerator;
	denumerator = denumerator*obj.denumerator;

	normalize();

	return *this;
}

rational & rational::operator-=(const rational &obj)
{
	this->operator+=(-obj);
	return *this;
}

rational & rational::operator*=(const rational &obj)
{
	assert(denumerator != 0);
	assert(obj.denumerator != 0);

	numerator *= obj.numerator;
	denumerator *= obj.denumerator;

	normalize();

	return *this;
}

rational & rational::operator/=(const rational &obj)
{
	this->operator*=(obj.invert());

	return *this;
}

rational rational::operator-() const
{
	return rational(-numerator, denumerator);
}

bool rational::more_then(const rational &obj) const
{
	long numer1 = numerator*obj.denumerator;
	long numer2 = obj.numerator*denumerator;

	return (numer1 > numer2);
}

bool rational::equals_to(const rational &obj) const
{
	return (numerator==obj.numerator)&&
			(denumerator==obj.denumerator);
}

void rational::print_rational(std::ostream & out) const
{
	out << numerator << "/" << denumerator;
}

void rational::read_rational(std::istream & in)
{
	char slash;
	char buffer[20];
	int pos = 0;

	numerator = denumerator = 0;

	while (isspace(in.peek())) in.ignore();
	if (in.peek() == '-') in >> buffer[pos++];
	while (isdigit(in.peek())) in >> buffer[pos++];
	numerator = atoi(buffer);
	if (in.peek() == '/') {
		in.ignore();
		if (isdigit(in.peek()))
			in >> denumerator;
	} else {
		denumerator = 1;
	}

	normalize();
}

rational rational::invert() const
{
	return rational(denumerator, numerator);
}

rational::operator double() const
{
	return double(numerator)/denumerator;
}

bool operator==(const rational &obj1, const rational &obj2)
{
	return obj1.equals_to(obj2);
}

bool operator!=(const rational &obj1, const rational &obj2)
{
	return !obj1.equals_to(obj2);
}

bool operator>=(const rational &obj1, const rational &obj2)
{
	return obj1.equals_to(obj2) || obj1.more_then(obj2);
}

bool operator<=(const rational &obj1, const rational &obj2)
{
	return !obj1.more_then(obj2);
}

bool operator>(const rational &obj1, const rational &obj2)
{
	return obj1.more_then(obj2);
}

bool operator<(const rational &obj1, const rational &obj2)
{
	return !(obj1>=obj2);
}

rational operator+(rational obj1, const rational &obj2)
{
	return (obj1+=obj2);
}

rational operator-(rational obj1, const rational &obj2)
{
	return (obj1-=obj2);
}

rational operator*(rational obj1, const rational &obj2)
{
	return (obj1*=obj2);
}

rational operator/(rational obj1, const rational &obj2)
{
	return (obj1/=obj2);
}

std::ostream & operator<<(std::ostream & out, const rational &obj)
{
	obj.print_rational(out);

	return out;
}

std::istream & operator>>(std::istream & in, rational &obj)
{
	obj.read_rational(in);

	return in;
}
